package com.sensoro.beacon.kit;

import com.sensoro.beacon.kit.Beacon.Proximity;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

class BeaconFactory {
    // private static final byte[] KEY = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    // 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
    private static final int RANDOM_CODE_LENGTH = 2;
    private static final int KEY_LENGTH = 14;
    private static final int KEY_ID_LENGTH = 2;

    private String mac;
    private int rssi;
    private byte[] scanData;
    private final Map<String, Beacon> iBeaconMap;
    private final Map<String, Beacon> e781Map;
    private HashMap<String, byte[]> broadcastKeyMap;

    public BeaconFactory(String mac, int rssi, byte[] scanData, Map<String, Beacon> iBeaconMap, Map<String, Beacon> e781Map, HashMap<String, byte[]> broadcastKeyMap) {
        this.mac = mac;
        this.rssi = rssi;
        this.scanData = scanData;
        this.iBeaconMap = iBeaconMap;
        this.e781Map = e781Map;
        this.broadcastKeyMap = broadcastKeyMap;
    }

    public Beacon createBeacon() {
        SensoroBLEData sensoroBLEData = parseScanData(scanData);
//        if(beacon == null || beacon.proximityUUID == null ){
//            return null;
//        } else {
//            if (!beacon.proximityUUID.toUpperCase().equals("00A01AF0-232A-4518-9C0E-323FB773F5EF".toUpperCase())){
//                return null;
//            }
//        }
        return parseSensoroBLEData2Beacon(sensoroBLEData);
    }

    private Beacon parseSensoroBLEData2Beacon(SensoroBLEData sensoroBLEData) {
        Beacon beacon = null;
        if (sensoroBLEData != null) {
            if (sensoroBLEData.iBeacon != null) {
                // Contain iBeacon.
                if (sensoroBLEData.sensoroE780 != null) {
                    // Contain E780, Yunzi 2.3 and below.
                    beacon = parseYunziBelow2_3(sensoroBLEData);
                } else if (sensoroBLEData.sensoroE781 != null) {
                    // Contain E781, multi mode.
                    beacon = parseMultiModeWithIBeacon(sensoroBLEData);
                } else {
                    // Just an iBeacon. Not support for this moment.
                    return null;
                }
            } else {
                // Not contain iBeacon.
                if (sensoroBLEData.sensoroE781 != null) {
                    // Contain E781, a sensoro sensor.
                    beacon = parseMultiModeWithoutIBeacon(sensoroBLEData);
                } else {
                    // Not a beacon.
                    return null;
                }
            }
        }

        if (beacon != null) {
            beacon.rssi = rssi;
            beacon.accuracy = calculateAccuracy(beacon.measuredPower, rssi);
            beacon.proximity = calculateProximity(beacon.accuracy);
            beacon.macAddress = mac;
        }
        return beacon;
    }

    /**
     * Multi mode of E781 with iBeacon.
     *
     * @param sensoroBLEData
     * @return
     */
    private Beacon parseMultiModeWithIBeacon(SensoroBLEData sensoroBLEData) {
        Beacon beacon = null;
        if (sensoroBLEData != null && sensoroBLEData.iBeacon != null && sensoroBLEData.sensoroE781 != null) {
            beacon = new Beacon();
            // parse iBeacon
            if (((int) sensoroBLEData.iBeacon[2] & 0xff) == 0x4c && ((int) sensoroBLEData.iBeacon[3] & 0xff) == 0x00 && ((int) sensoroBLEData.iBeacon[4] & 0xff) == 0x02 && ((int) sensoroBLEData.iBeacon[5] & 0xff) == 0x15) {
                // measuredPower
                beacon.measuredPower = (int) sensoroBLEData.iBeacon[26];
                // proximityUUID
                byte[] proximityUuidBytes = new byte[16];
                System.arraycopy(sensoroBLEData.iBeacon, 6, proximityUuidBytes, 0, 16);
                String hexString = SensoroUtils.bytesToHex(proximityUuidBytes);
                StringBuilder sb = new StringBuilder();
                sb.append(hexString.substring(0, 8));
                sb.append("-");
                sb.append(hexString.substring(8, 12));
                sb.append("-");
                sb.append(hexString.substring(12, 16));
                sb.append("-");
                sb.append(hexString.substring(16, 20));
                sb.append("-");
                sb.append(hexString.substring(20, 32));
                beacon.proximityUUID = sb.toString().toUpperCase();
            } else {
                return null;
            }
            // Parse E781.
            if (((int) sensoroBLEData.sensoroE781[2] & 0xff) == 0xe7 && ((int) sensoroBLEData.sensoroE781[3] & 0xff) == 0x81) {
                // 硬件版本
                int hardwareCode = (int) sensoroBLEData.sensoroE781[4] & 0xff;
                beacon.hardwareModelName = Integer.toHexString(hardwareCode).toUpperCase();

                // 固件版本
                int firmwareCode = (int) sensoroBLEData.sensoroE781[5] & 0xff;
                beacon.firmwareVersion = Integer.toHexString(firmwareCode / 16).toUpperCase() + "." + Integer.toHexString(firmwareCode % 16).toUpperCase();
                switch ((int) sensoroBLEData.sensoroE781[6] & 0x70) {
                    case 0x10: // 0001 0000 (mode=1)
                        if (isAbove3_0(beacon)) {
                            if ((sensoroBLEData.sensoroE781[9] & 0xff) == 0 && (sensoroBLEData.sensoroE781[10] & 0xff) == 0) {
                                // 未开启加密
                                // SN
                                byte[] sn = new byte[3];
                                System.arraycopy(sensoroBLEData.sensoroE781, 11, sn, 0, sn.length);
                                beacon.serialNumber = parseSN(sn);
                                // major
                                beacon.major = ((sensoroBLEData.sensoroE781[17] & 0xff) << 8) + (sensoroBLEData.sensoroE781[18] & 0xff);
                                // minor
                                beacon.minor = ((sensoroBLEData.sensoroE781[19] & 0xff) << 8) + (sensoroBLEData.sensoroE781[20] & 0xff);
                                // 电量
                                beacon.batteryLevel = sensoroBLEData.sensoroE781[21] & 0xff;
                                // 位域
                                BitFields bitFields = parseBitFields(sensoroBLEData.sensoroE781[22], sensoroBLEData.sensoroE781[23]);
                                BaseSettings baseSettings = new BaseSettings();
                                // 功率
                                baseSettings.setTransmitPower(SensoroUtils.getTransmitPower(bitFields.transmitPower));
                                beacon.transmitPower = SensoroUtils.getTransmitPower(bitFields.transmitPower);
                                // 频率
                                baseSettings.setAdvertisingInterval(SensoroUtils.getAdvertisingInterval(bitFields.advertisingInterval));
                                beacon.advertisingInterval = SensoroUtils.getAdvertisingInterval(bitFields.advertisingInterval);
                                beacon.baseSettings = baseSettings;
                                // 是否开启密码
                                beacon.isPasswordEnabled = bitFields.isPasswordEnabled;
                                // 是否开启加密
                                beacon.isSecretEnabled = bitFields.isSecretEnabled;
                                // 是否开启节能模式
                                beacon.isEnergySavingEnabled = bitFields.isEnergySavingEnabled;
                                beacon.energySavingMode = getEnergySavingMode(beacon.isEnergySavingEnabled);
                                // 模式1 运动状态为关闭, 加速度计数器为0.
                                beacon.movingState = Beacon.MovingState.DISABLED;
                                beacon.accelerometerCount = 0;
                            } else {
                                // 开启加密
                                byte[] keyIdBytes = new byte[2];
                                System.arraycopy(sensoroBLEData.sensoroE781, 7, keyIdBytes, 0, keyIdBytes.length);
                                byte[] keyBytes = parseBroadcastKey(keyIdBytes, broadcastKeyMap);
                                if (keyBytes == null || keyBytes.length != KEY_LENGTH) {
                                    return null;
                                }

                                byte[] encrypt = new byte[16];
                                System.arraycopy(sensoroBLEData.sensoroE781, 11, encrypt, 0, encrypt.length);
                                byte[] key = new byte[16];
                                System.arraycopy(keyBytes, 0, key, 0, keyBytes.length);
                                System.arraycopy(sensoroBLEData.sensoroE781, 9, key, 14, RANDOM_CODE_LENGTH);
                                byte[] decrypt = SensoroUtils.decrypt_AES_128(encrypt, key);

                                // SN
                                byte[] sn = new byte[3];
                                System.arraycopy(decrypt, 0, sn, 0, sn.length);
                                beacon.serialNumber = parseSN(sn);
                                // major
                                beacon.major = ((decrypt[6] & 0xff) << 8) + (decrypt[7] & 0xff);
                                // minor
                                beacon.minor = ((decrypt[8] & 0xff) << 8) + (decrypt[9] & 0xff);
                                // 电量
                                beacon.batteryLevel = decrypt[10] & 0xff;
                                // 位域
                                BitFields bitFields = parseBitFields(decrypt[11], decrypt[12]);
                                BaseSettings baseSettings = new BaseSettings();
                                // 功率
                                baseSettings.setTransmitPower(SensoroUtils.getTransmitPower(bitFields.transmitPower));
                                beacon.transmitPower = SensoroUtils.getTransmitPower(bitFields.transmitPower);
                                // 频率
                                baseSettings.setAdvertisingInterval(SensoroUtils.getAdvertisingInterval(bitFields.advertisingInterval));
                                beacon.advertisingInterval = SensoroUtils.getAdvertisingInterval(bitFields.advertisingInterval);
                                beacon.baseSettings = baseSettings;
                                // 是否开启密码
                                beacon.isPasswordEnabled = bitFields.isPasswordEnabled;
                                // 是否开启加密
                                beacon.isSecretEnabled = bitFields.isSecretEnabled;
                                // 是否开启节能模式
                                beacon.isEnergySavingEnabled = bitFields.isEnergySavingEnabled;
                                beacon.energySavingMode = getEnergySavingMode(beacon.isEnergySavingEnabled);
                                // 模式1 运动状态为关闭, 加速度计数器为0.
                                beacon.movingState = Beacon.MovingState.DISABLED;
                                beacon.accelerometerCount = 0;
                                // CRC8
                                int crc8 = CRC8.compute(sn);
                                if ((decrypt[15] & 0xff) != crc8) {
                                    return null;
                                }
                            }
                        } else {
                            return null;
                        }

                        break;
                    case 0x30: // 0011 0000 (mode=3)
                        if (isAbove3_0(beacon)) {
                            // SN
                            byte[] sn = new byte[3];
                            System.arraycopy(sensoroBLEData.sensoroE781, 7, sn, 0, sn.length);
                            beacon.serialNumber = parseSN(sn);
                            // major
                            beacon.major = ((sensoroBLEData.sensoroE781[13] & 0xff) << 8) + (sensoroBLEData.sensoroE781[14] & 0xff);
                            // minor
                            beacon.minor = ((sensoroBLEData.sensoroE781[15] & 0xff) << 8) + (sensoroBLEData.sensoroE781[16] & 0xff);
                            // 电量
                            beacon.batteryLevel = sensoroBLEData.sensoroE781[17] & 0xff;
                            // 位域
                            BitFields bitFields = parseBitFields(sensoroBLEData.sensoroE781[18], sensoroBLEData.sensoroE781[19]);
                            BaseSettings baseSettings = new BaseSettings();
                            // 功率
                            baseSettings.setTransmitPower(SensoroUtils.getTransmitPower(bitFields.transmitPower));
                            beacon.transmitPower = SensoroUtils.getTransmitPower(bitFields.transmitPower);
                            // 频率
                            baseSettings.setAdvertisingInterval(SensoroUtils.getAdvertisingInterval(bitFields.advertisingInterval));
                            beacon.advertisingInterval = SensoroUtils.getAdvertisingInterval(bitFields.advertisingInterval);
                            beacon.baseSettings = baseSettings;
                            // 是否开启密码
                            beacon.isPasswordEnabled = bitFields.isPasswordEnabled;
                            // 是否开启加密
                            beacon.isSecretEnabled = bitFields.isSecretEnabled;
                            // 是否开启节能模式
                            beacon.isEnergySavingEnabled = bitFields.isEnergySavingEnabled;
                            beacon.energySavingMode = getEnergySavingMode(beacon.isEnergySavingEnabled);
                            // 温度
                            beacon.temperature = parseTemperature(sensoroBLEData.sensoroE781[20]);
                            // 光线原始值
                            beacon.light = parseBrightnessLux(sensoroBLEData.sensoroE781[21], sensoroBLEData.sensoroE781[22]);
                            // 加速度计数器
                            beacon.accelerometerCount = (sensoroBLEData.sensoroE781[23] & 0xff) + ((sensoroBLEData.sensoroE781[24] & 0xff) << 8);
                            // 加速度变化
                            beacon.movingState = parseMovingState(sensoroBLEData.sensoroE781[25]);
                        } else {
                            return null;
                        }
                        break;
                    case 0x40: // 0100 0000 (mode=4)
                        if (((int) sensoroBLEData.sensoroE781[6] & 0xff) == 0x41) {
                            // 0100 0001(mode=4,id=1)
                            if (isAbove3_0(beacon)) {
                                if ((sensoroBLEData.sensoroE781[9] & 0xff) == 0 && (sensoroBLEData.sensoroE781[10] & 0xff) == 0) {
                                    // 未开启加密
                                    return null;
                                } else {
                                    // 开启加密
                                    byte[] keyIdBytes = new byte[2];
                                    System.arraycopy(sensoroBLEData.sensoroE781, 7, keyIdBytes, 0, keyIdBytes.length);
                                    byte[] keyBytes = parseBroadcastKey(keyIdBytes, broadcastKeyMap);
                                    if (keyBytes == null || keyBytes.length != KEY_LENGTH) {
                                        return null;
                                    }

                                    byte[] encrypt = new byte[16];
                                    System.arraycopy(sensoroBLEData.sensoroE781, 11, encrypt, 0, encrypt.length);
                                    byte[] key = new byte[16];
                                    System.arraycopy(keyBytes, 0, key, 0, keyBytes.length);
                                    System.arraycopy(sensoroBLEData.sensoroE781, 9, key, 14, RANDOM_CODE_LENGTH);
                                    byte[] decrypt = SensoroUtils.decrypt_AES_128(encrypt, key);

                                    // SN
                                    byte[] sn = new byte[3];
                                    System.arraycopy(decrypt, 0, sn, 0, sn.length);
                                    beacon.serialNumber = parseSN(sn);
                                    // major
                                    beacon.major = ((decrypt[6] & 0xff) << 8) + (decrypt[7] & 0xff);
                                    // minor
                                    beacon.minor = ((decrypt[8] & 0xff) << 8) + (decrypt[9] & 0xff);
                                    // 电量
                                    beacon.batteryLevel = decrypt[10] & 0xff;
                                    // 位域
                                    BitFields bitFields = parseBitFields(decrypt[11], decrypt[12]);
                                    BaseSettings baseSettings = new BaseSettings();
                                    // 功率
                                    baseSettings.setTransmitPower(SensoroUtils.getTransmitPower(bitFields.transmitPower));
                                    beacon.transmitPower = SensoroUtils.getTransmitPower(bitFields.transmitPower);
                                    // 频率
                                    baseSettings.setAdvertisingInterval(SensoroUtils.getAdvertisingInterval(bitFields.advertisingInterval));
                                    beacon.advertisingInterval = SensoroUtils.getAdvertisingInterval(bitFields.advertisingInterval);
                                    beacon.baseSettings = baseSettings;
                                    // 是否开启密码
                                    beacon.isPasswordEnabled = bitFields.isPasswordEnabled;
                                    // 是否开启加密
                                    beacon.isSecretEnabled = bitFields.isSecretEnabled;
                                    // 是否开启节能模式
                                    beacon.isEnergySavingEnabled = bitFields.isEnergySavingEnabled;
                                    beacon.energySavingMode = getEnergySavingMode(beacon.isEnergySavingEnabled);
                                    // CRC8
                                    int crc8 = CRC8.compute(sn);
                                    if ((decrypt[15] & 0xff) != crc8) {
                                        return null;
                                    }
                                    // Mode=4,id=1解析成功,查找 e781map.
                                    synchronized (e781Map) {
                                        Beacon containBeacon = e781Map.get(beacon.serialNumber);
                                        if (containBeacon == null) {
                                            // 没找到 e781 部分，保存 iBeacon部分
                                            iBeaconMap.put(beacon.serialNumber, beacon);
                                            return null;
                                        } else {
                                            // 找到 e781 部分.生成 beacon
                                            beacon.serialNumber = containBeacon.serialNumber;
                                            beacon.batteryLevel = containBeacon.batteryLevel;
                                            beacon.temperature = containBeacon.temperature;
                                            beacon.light = containBeacon.light;
                                            beacon.accelerometerCount = containBeacon.accelerometerCount;
                                            beacon.movingState = containBeacon.movingState;
                                            beacon.measuredPower = containBeacon.measuredPower;
                                        }
                                    }
                                }
                            } else {
                                // Not mode=4,id=1.
                                return null;
                            }
                        } else {
                            return null;
                        }
                        break;

                    default:
                        // Just an iBeacon. Not support at this moment.
                        return null;
                }
            }
        }
        return beacon;
    }

    /**
     * Multi mode of E781 without iBeacon.
     *
     * @param sensoroBLEData
     * @return
     */
    private Beacon parseMultiModeWithoutIBeacon(SensoroBLEData sensoroBLEData) {
        Beacon beacon = null;
        if (sensoroBLEData != null && sensoroBLEData.sensoroE781 != null) {
            beacon = new Beacon();
            // Parse E781.
            if (((int) sensoroBLEData.sensoroE781[2] & 0xff) == 0xe7 && ((int) sensoroBLEData.sensoroE781[3] & 0xff) == 0x81) {
                // 硬件版本
                int hardwareCode = (int) sensoroBLEData.sensoroE781[4] & 0xff;
                beacon.hardwareModelName = Integer.toHexString(hardwareCode).toUpperCase();

                // 固件版本
                int firmwareCode = (int) sensoroBLEData.sensoroE781[5] & 0xff;
                beacon.firmwareVersion = Integer.toHexString(firmwareCode / 16).toUpperCase() + "." + Integer.toHexString(firmwareCode % 16).toUpperCase();

                switch ((int) sensoroBLEData.sensoroE781[6] & 0x70) {
                    case 0x20: // 0010 0000 (mode=2)
                        if (isAbove3_0(beacon)) {
                            if ((sensoroBLEData.sensoroE781[9] & 0xff) == 0 && (sensoroBLEData.sensoroE781[10] & 0xff) == 0) {
                                // 未开启加密
                                // SN
                                byte[] sn = new byte[3];
                                System.arraycopy(sensoroBLEData.sensoroE781, 11, sn, 0, sn.length);
                                beacon.serialNumber = parseSN(sn);
                                // 电量
                                beacon.batteryLevel = sensoroBLEData.sensoroE781[14] & 0xff;
                                // 位域
                                BitFields bitFields = parseBitFields(sensoroBLEData.sensoroE781[15], sensoroBLEData.sensoroE781[16]);
                                BaseSettings baseSettings = new BaseSettings();
                                // 功率
                                baseSettings.setTransmitPower(SensoroUtils.getTransmitPower(bitFields.transmitPower));
                                beacon.transmitPower = SensoroUtils.getTransmitPower(bitFields.transmitPower);
                                // 频率
                                baseSettings.setAdvertisingInterval(SensoroUtils.getAdvertisingInterval(bitFields.advertisingInterval));
                                beacon.advertisingInterval = SensoroUtils.getAdvertisingInterval(bitFields.advertisingInterval);
                                beacon.baseSettings = baseSettings;
                                // 是否开启密码
                                beacon.isPasswordEnabled = bitFields.isPasswordEnabled;
                                // 是否开启加密
                                beacon.isSecretEnabled = bitFields.isSecretEnabled;
                                // 是否开启节能模式
                                beacon.isEnergySavingEnabled = bitFields.isEnergySavingEnabled;
                                beacon.energySavingMode = getEnergySavingMode(beacon.isEnergySavingEnabled);
                                // 温度
                                beacon.temperature = parseTemperature(sensoroBLEData.sensoroE781[17]);
                                // 光线原始值
                                beacon.light = parseBrightnessLux(sensoroBLEData.sensoroE781[18], sensoroBLEData.sensoroE781[19]);
                                // 加速度计数器
                                beacon.accelerometerCount = (sensoroBLEData.sensoroE781[20] & 0xff) + ((sensoroBLEData.sensoroE781[21] & 0xff) << 8);
                                // 加速度变化
                                beacon.movingState = parseMovingState(sensoroBLEData.sensoroE781[22]);
                                // mrssi
                                beacon.measuredPower = (int) sensoroBLEData.sensoroE781[23];
                            } else {
                                // 开启加密
                                byte[] keyIdBytes = new byte[2];
                                System.arraycopy(sensoroBLEData.sensoroE781, 7, keyIdBytes, 0, keyIdBytes.length);
                                byte[] keyBytes = parseBroadcastKey(keyIdBytes, broadcastKeyMap);
                                if (keyBytes == null || keyBytes.length != KEY_LENGTH) {
                                    return null;
                                }

                                byte[] encrypt = new byte[16];
                                System.arraycopy(sensoroBLEData.sensoroE781, 11, encrypt, 0, encrypt.length);
                                byte[] key = new byte[16];
                                System.arraycopy(keyBytes, 0, key, 0, keyBytes.length);
                                System.arraycopy(sensoroBLEData.sensoroE781, 9, key, 14, RANDOM_CODE_LENGTH);
                                byte[] decrypt = SensoroUtils.decrypt_AES_128(encrypt, key);
                                // SN
                                byte[] sn = new byte[3];
                                System.arraycopy(decrypt, 0, sn, 0, sn.length);
                                beacon.serialNumber = parseSN(sn);
                                // 电量
                                beacon.batteryLevel = decrypt[3] & 0xff;
                                // 位域
                                BitFields bitFields = parseBitFields(decrypt[4], decrypt[5]);
                                BaseSettings baseSettings = new BaseSettings();
                                // 功率
                                baseSettings.setTransmitPower(SensoroUtils.getTransmitPower(bitFields.transmitPower));
                                beacon.transmitPower = SensoroUtils.getTransmitPower(bitFields.transmitPower);
                                // 频率
                                baseSettings.setAdvertisingInterval(SensoroUtils.getAdvertisingInterval(bitFields.advertisingInterval));
                                beacon.advertisingInterval = SensoroUtils.getAdvertisingInterval(bitFields.advertisingInterval);
                                beacon.baseSettings = baseSettings;
                                // 是否开启密码
                                beacon.isPasswordEnabled = bitFields.isPasswordEnabled;
                                // 是否开启加密
                                beacon.isSecretEnabled = bitFields.isSecretEnabled;
                                // 是否开启节能模式
                                beacon.isEnergySavingEnabled = bitFields.isEnergySavingEnabled;
                                beacon.energySavingMode = getEnergySavingMode(beacon.isEnergySavingEnabled);
                                // 温度
                                beacon.temperature = parseTemperature(decrypt[6]);
                                // 光线原始值
                                beacon.light = parseBrightnessLux(decrypt[7], decrypt[8]);
                                // 加速度计数器
                                beacon.accelerometerCount = (decrypt[9] & 0xff) + ((decrypt[10] & 0xff) << 8);
                                // 加速度变化
                                beacon.movingState = parseMovingState(decrypt[11]);
                                // mrssi
                                beacon.measuredPower = (int) decrypt[12];
                                // CRC8
                                int crc8 = CRC8.compute(sn);
                                if ((decrypt[15] & 0xff) != crc8) {
                                    return null;
                                }
                            }
                        } else {
                            return null;
                        }

                        break;
                    case 0x40: // 0100 0000 (mode=4)
                        if (((int) sensoroBLEData.sensoroE781[6] & 0xff) == 0x42) {
                            // 0100 0010(mode=4,id=2)
                            if (isAbove3_0(beacon)) {
                                if ((sensoroBLEData.sensoroE781[9] & 0xff) == 0 && (sensoroBLEData.sensoroE781[10] & 0xff) == 0) {
                                    // 未开启加密
                                    return null;
                                } else {
                                    // 开启加密
                                    byte[] keyIdBytes = new byte[2];
                                    System.arraycopy(sensoroBLEData.sensoroE781, 7, keyIdBytes, 0, keyIdBytes.length);
                                    byte[] keyBytes = parseBroadcastKey(keyIdBytes, broadcastKeyMap);
                                    if (keyBytes == null || keyBytes.length != KEY_LENGTH) {
                                        return null;
                                    }

                                    byte[] encrypt = new byte[16];
                                    System.arraycopy(sensoroBLEData.sensoroE781, 11, encrypt, 0, encrypt.length);
                                    byte[] key = new byte[16];
                                    System.arraycopy(keyBytes, 0, key, 0, keyBytes.length);
                                    System.arraycopy(sensoroBLEData.sensoroE781, 9, key, 14, RANDOM_CODE_LENGTH);
                                    byte[] decrypt = SensoroUtils.decrypt_AES_128(encrypt, key);
                                    // SN
                                    byte[] sn = new byte[3];
                                    System.arraycopy(decrypt, 0, sn, 0, sn.length);
                                    beacon.serialNumber = parseSN(sn);
                                    // 电量
                                    beacon.batteryLevel = decrypt[3] & 0xff;
                                    // 位域
                                    BitFields bitFields = parseBitFields(decrypt[4], decrypt[5]);
                                    BaseSettings baseSettings = new BaseSettings();
                                    // 功率
                                    baseSettings.setTransmitPower(SensoroUtils.getTransmitPower(bitFields.transmitPower));
                                    beacon.transmitPower = SensoroUtils.getTransmitPower(bitFields.transmitPower);
                                    // 频率
                                    baseSettings.setAdvertisingInterval(SensoroUtils.getAdvertisingInterval(bitFields.advertisingInterval));
                                    beacon.advertisingInterval = SensoroUtils.getAdvertisingInterval(bitFields.advertisingInterval);
                                    beacon.baseSettings = baseSettings;
                                    // 是否开启密码
                                    beacon.isPasswordEnabled = bitFields.isPasswordEnabled;
                                    // 是否开启加密
                                    beacon.isSecretEnabled = bitFields.isSecretEnabled;
                                    // 是否开启节能模式
                                    beacon.isEnergySavingEnabled = bitFields.isEnergySavingEnabled;
                                    beacon.energySavingMode = getEnergySavingMode(beacon.isEnergySavingEnabled);
                                    // 温度
                                    beacon.temperature = parseTemperature(decrypt[6]);
                                    // 光线原始值
                                    beacon.light = parseBrightnessLux(decrypt[7], decrypt[8]);
                                    // 加速度计数器
                                    beacon.accelerometerCount = (decrypt[9] & 0xff) + ((decrypt[10] & 0xff) << 8);
                                    // 加速度变化
                                    beacon.movingState = parseMovingState(decrypt[11]);
                                    // mrssi
                                    beacon.measuredPower = (int) decrypt[12];
                                    // CRC8
                                    int crc8 = CRC8.compute(sn);
                                    if ((decrypt[15] & 0xff) != crc8) {
                                        return null;
                                    }

                                    // Mode=4,id=2解析成功,查找 iBeaconMap.
                                    synchronized (iBeaconMap) {
                                        Beacon containBeacon = iBeaconMap.get(beacon.serialNumber);
                                        if (containBeacon == null) {
                                            // 没找到 iBeacon 部分，保存 e781 部分
                                            e781Map.put(beacon.serialNumber, beacon);
                                            return null;
                                        } else {
                                            // 找到 iBeacon 部分.生成 beacon
                                            beacon.serialNumber = containBeacon.serialNumber;
                                            beacon.major = containBeacon.major;
                                            beacon.minor = containBeacon.minor;
                                            beacon.proximityUUID = containBeacon.proximityUUID;
                                            beacon.batteryLevel = containBeacon.batteryLevel;
                                        }
                                    }
                                }
                            } else {
                                // Not mode=4,id=2.
                                return null;
                            }
                        } else {
                            return null;
                        }
                        break;

                    default:
                        // Just an iBeacon. Not support at this moment.
                        return null;
                }
            }
        }
        return beacon;
    }

    /**
     * Yunzi 2.3 and below.(A0-1.0,B0-1.0,B0-2.0,B0-2.1,B0-2.2,B0-2.3)
     *
     * @param sensoroBLEData
     * @return
     */
    private Beacon parseYunziBelow2_3(SensoroBLEData sensoroBLEData) {
        Beacon beacon = null;
        if (sensoroBLEData != null && sensoroBLEData.iBeacon != null && sensoroBLEData.sensoroE780 != null) {
            beacon = new Beacon();
            // parse iBeacon
            if (((int) sensoroBLEData.iBeacon[2] & 0xff) == 0x4c && ((int) sensoroBLEData.iBeacon[3] & 0xff) == 0x00 && ((int) sensoroBLEData.iBeacon[4] & 0xff) == 0x02 && ((int) sensoroBLEData.iBeacon[5] & 0xff) == 0x15) {
                // measuredPower
                beacon.measuredPower = (int) sensoroBLEData.iBeacon[26];
                // proximityUUID
                byte[] proximityUuidBytes = new byte[16];
                System.arraycopy(sensoroBLEData.iBeacon, 6, proximityUuidBytes, 0, 16);
                String hexString = SensoroUtils.bytesToHex(proximityUuidBytes);
                StringBuilder sb = new StringBuilder();
                sb.append(hexString.substring(0, 8));
                sb.append("-");
                sb.append(hexString.substring(8, 12));
                sb.append("-");
                sb.append(hexString.substring(12, 16));
                sb.append("-");
                sb.append(hexString.substring(16, 20));
                sb.append("-");
                sb.append(hexString.substring(20, 32));
                beacon.proximityUUID = sb.toString().toUpperCase();
            } else {
                return null;
            }
            // parse E780

            // 解决出厂未配置 beacon 长度为 8，导致 sdk 异常的问题
            if (!(sensoroBLEData.sensoroE780.length == 27 || sensoroBLEData.sensoroE780.length == 31)){
                return null;
            }

            if (((int) sensoroBLEData.sensoroE780[2] & 0xff) == 0xe7 && ((int) sensoroBLEData.sensoroE780[3] & 0xff) == 0x80) { // 是否为sensoro
                // 硬件版本
                int hardwareCode = (int) sensoroBLEData.sensoroE780[4] & 0xff;
                beacon.hardwareModelName = Integer.toHexString(hardwareCode).toUpperCase();

                // 固件版本
                int firmwareCode = (int) sensoroBLEData.sensoroE780[5] & 0xff;
                beacon.firmwareVersion = Integer.toHexString(firmwareCode / 16).toUpperCase() + "." + Integer.toHexString(firmwareCode % 16).toUpperCase();

                // major
                beacon.major = ((sensoroBLEData.sensoroE780[12] & 0xff) << 8) + (sensoroBLEData.sensoroE780[13] & 0xff);
                // minor
                beacon.minor = ((sensoroBLEData.sensoroE780[14] & 0xff) << 8) + (sensoroBLEData.sensoroE780[15] & 0xff);

                if (beacon.firmwareVersion.equals(Beacon.FV_10) && beacon.hardwareModelName.equals(Beacon.HV_A0)) { // A0-1.0
                    // SN
                    byte[] sn = new byte[6];
                    System.arraycopy(sensoroBLEData.sensoroE780, 16, sn, 0, sn.length);
                    beacon.serialNumber = parseSN(sn);
                    // 电量
                    beacon.batteryLevel = ((int) sensoroBLEData.sensoroE780[22] & 0xff);
                    // 温度
                    beacon.temperature = null;
                    // 光线原始值
                    beacon.light = null;
                    // 加速度计数器
                    beacon.accelerometerCount = 0;
                    // 加速度变化
                    beacon.movingState = Beacon.MovingState.DISABLED;
                } else if (beacon.firmwareVersion.equals(Beacon.FV_10) && beacon.hardwareModelName.equals(Beacon.HV_B0)) { // B0-1.0
                    // SN
                    byte[] sn = new byte[6];
                    System.arraycopy(sensoroBLEData.sensoroE780, 16, sn, 0, sn.length);
                    beacon.serialNumber = parseSN(sn);
                    // 电量
                    beacon.batteryLevel = ((int) sensoroBLEData.sensoroE780[22] & 0xff);
                    // 温度
                    beacon.temperature = parseTemperature(sensoroBLEData.sensoroE780[23]);
                    // 光线原始值
                    beacon.light = parseBrightnessLux(sensoroBLEData.sensoroE780[24], sensoroBLEData.sensoroE780[29]);
                    // 加速度计数器
                    beacon.accelerometerCount = (sensoroBLEData.sensoroE780[25] & 0xff) + ((sensoroBLEData.sensoroE780[26] & 0xff) << 8) + ((sensoroBLEData.sensoroE780[27] & 0xff) << 16);
                    // 加速度变化
                    beacon.movingState = parseMovingState(sensoroBLEData.sensoroE780[28]);
                } else if (beacon.firmwareVersion.substring(0, 1).equals("2") && beacon.hardwareModelName.equals(Beacon.HV_B0)) { // B0-2.x
                    // SN
                    byte[] sn = new byte[3];
                    System.arraycopy(sensoroBLEData.sensoroE780, 16, sn, 0, sn.length);
                    beacon.serialNumber = parseSN(sn);
                    // 电量
                    beacon.batteryLevel = ((int) sensoroBLEData.sensoroE780[19] & 0xff);
                    // 温度
                    beacon.temperature = parseTemperature(sensoroBLEData.sensoroE780[20]);
                    // 光线原始值
                    beacon.light = parseBrightnessLux(sensoroBLEData.sensoroE780[21], sensoroBLEData.sensoroE780[22]);
                    // B0-2.2 以下版本
                    if (beacon.firmwareVersion.equals(Beacon.FV_20) || beacon.firmwareVersion.equals(Beacon.FV_21) || beacon.firmwareVersion.equals(Beacon.FV_22)) {
                        // 加速度计数器
                        beacon.accelerometerCount = (sensoroBLEData.sensoroE780[23] & 0xff) + ((sensoroBLEData.sensoroE780[24] & 0xff) << 8) + ((sensoroBLEData.sensoroE780[25] & 0xff) << 16);
                        // 加速度变化
                        beacon.movingState = parseMovingState(sensoroBLEData.sensoroE780[26]);
                    } else if (beacon.firmwareVersion.equals(Beacon.FV_23)) {
                        // B0-2.3
                        // 加速度计数器
                        beacon.accelerometerCount = (sensoroBLEData.sensoroE780[23] & 0xff) + ((sensoroBLEData.sensoroE780[24] & 0xff) << 8);
                        // 加速度变化
                        beacon.movingState = parseMovingState(sensoroBLEData.sensoroE780[25]);
                        // 是否开启防蹭用
                        beacon.isSecretEnabled = parseSecretEnable(sensoroBLEData.sensoroE780[26]);
                    }
                } else {
                    // Not A0-1.0,B0-1.0,B0-2.0,B0-2.1,B0-2.2,B0-2.3.
                    return null;
                }
            } else {
                // Not sensoro E780.
                return null;
            }
        }
        return beacon;
    }

    /**
     * B0-3.0,B0-3.1,C0-3.0,C0-3.1
     * @param beacon
     * @return
     */
    private boolean isAbove3_0(Beacon beacon){
        if ((beacon.hardwareModelName.equals(Beacon.HV_B0) && beacon.firmwareVersion.equals(Beacon.FV_30))
                || (beacon.hardwareModelName.equals(Beacon.HV_B0) && beacon.firmwareVersion.equals(Beacon.FV_31))
                || (beacon.hardwareModelName.equals(Beacon.HV_C0) && beacon.firmwareVersion.equals(Beacon.FV_30))
                || (beacon.hardwareModelName.equals(Beacon.HV_C0) && beacon.firmwareVersion.equals(Beacon.FV_31))){
            return true;
        } else {
            return false;
        }
    }

    private boolean parseSecretEnable(byte b) {
        return (int) b != 0;
    }

    private String parseSN(byte[] sn) {
        String serialNumber = null;
        if (sn.length == 3) {
            serialNumber = "0117C5" + SensoroUtils.bytesToHex(sn);
        } else if (sn.length == 6) {
            serialNumber = SensoroUtils.bytesToHex(sn);
        }
        return serialNumber != null ? serialNumber.toUpperCase() : null;
    }

    /**
     * 解析位域
     *
     * @param bitFieldsByteHigh
     * @param bitFieldsByteLow
     * @return
     */
    private BitFields parseBitFields(byte bitFieldsByteHigh, byte bitFieldsByteLow) {
        BitFields bitFields = new BitFields();
        bitFields.transmitPower = (bitFieldsByteHigh & 0xf0) >> 4;
        bitFields.advertisingInterval = (bitFieldsByteHigh & 0x0f);
        bitFields.isPasswordEnabled = ((bitFieldsByteLow & 0x80) != 0);
        bitFields.isSecretEnabled = ((bitFieldsByteLow & 0x40) != 0);
        bitFields.isEnergySavingEnabled = ((bitFieldsByteLow & 0x20) != 0);
        return bitFields;
    }

    /**
     * 解析 beacon 温度
     *
     * @param temperatureByte
     * @return
     */
    private Integer parseTemperature(byte temperatureByte) {
        int temperature = ((int) temperatureByte & 0xff);
        if (temperature == 0xff) { // 温度传感器关闭
            return null;
        } else {
            return temperature - 10; // 实际温度-10
        }
    }

    /**
     * 解析 beacon 运动状态
     *
     * @param movingStateByte
     * @return
     */
    private Beacon.MovingState parseMovingState(byte movingStateByte) {
        int movingState = ((int) movingStateByte & 0xff);
        return Beacon.MovingState.getMovingState(movingState);
    }

    /**
     * 解析 beacon 光线原始数值
     *
     * @param luxHighByte
     * @param luxLowByte
     * @return
     */
    private Double parseBrightnessLux(byte luxHighByte, byte luxLowByte) {
        int luxRawHigh = ((int) luxHighByte & 0xff);
        int luxRawLow = ((int) luxLowByte & 0xff);
        if (luxRawHigh == 0xff) { // 光线传感器关闭
            return null;
        } else {
            return calculateLux(luxRawHigh, luxRawLow);
        }
    }

    private double calculateLux(int luxRawHigh, int luxRawLow) {
        double light = Math.pow(2, luxRawHigh / 16) * ((luxRawHigh % 16) * 16 + luxRawLow % 16) * 0.045;
        BigDecimal bigDecimal = new BigDecimal(Double.toString(light)).setScale(3, BigDecimal.ROUND_HALF_UP);
        return bigDecimal.doubleValue();
    }

    private double calculateAccuracy(int txPower, double rssi) {
        if (txPower == 0){
            return -1.0;
        }
        if (rssi == 0) {
            return -1.0; // 无法确定距离,返回-1
        }

        double accuracy;
        double ratio = rssi * 1.0 / txPower;
        if (ratio < 1.0) {
            accuracy = Math.pow(ratio, 10);
        } else {
            accuracy = (0.89976) * Math.pow(ratio, 7.7095) + 0.111;
        }
        return new BigDecimal(Double.toString(accuracy)).setScale(4, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    private BaseSettings.EnergySavingMode getEnergySavingMode(boolean isEnergySavingEnabled) {
        if (isEnergySavingEnabled){
            return BaseSettings.EnergySavingMode.LIGHT_SENSOR;
        } else {
            return BaseSettings.EnergySavingMode.NONE;
        }
    }

    private Proximity calculateProximity(double accuracy) {
        if (accuracy < 0) {
            return Proximity.PROXIMITY_UNKNOWN;
        }
        if (accuracy < 0.5) {
            return Proximity.PROXIMITY_IMMEDIATE;
        }
        if (accuracy <= 4.0) {
            return Proximity.PROXIMITY_NEAR;
        }
        return Proximity.PROXIMITY_FAR;

    }

    private SensoroBLEData parseScanData(byte[] scanData) {
        SensoroBLEData sensoroBLEData;
        ArrayList<byte[]> bleDataArrayList = parseScanData2BLEList(scanData);
        sensoroBLEData = parseBLEDataList2SensoroBLEData(bleDataArrayList);
        return sensoroBLEData;
    }

    private SensoroBLEData parseBLEDataList2SensoroBLEData(ArrayList<byte[]> bleDataArrayList) {
        SensoroBLEData sensoroBLEData = null;
        if (bleDataArrayList != null && bleDataArrayList.size() != 0) {
            sensoroBLEData = new SensoroBLEData();
            for (byte[] data : bleDataArrayList) {
                if (((data[1] & 0xff) == 0xff) && ((data[2] & 0xff) == 0x4c) && ((data[3] & 0xff) == 0x00) && ((data[4] & 0xff) == 0x02)) {
                    // iBeacon
                    int length = data[0] & 0xff;
                    sensoroBLEData.iBeacon = new byte[length + 1];
                    System.arraycopy(data, 0, sensoroBLEData.iBeacon, 0, length + 1);
                } else if (((data[1] & 0xff) == 0x16) && ((data[2] & 0xff) == 0xe7) && ((data[3] & 0xff) == 0x80)) {
                    // e780
                    int length = data[0] & 0xff;
                    sensoroBLEData.sensoroE780 = new byte[length + 1];
                    System.arraycopy(data, 0, sensoroBLEData.sensoroE780, 0, length + 1);
                } else if (((data[1] & 0xff) == 0x16) && ((data[2] & 0xff) == 0xe7) && ((data[3] & 0xff) == 0x81)) {
                    // e781
                    int length = data[0] & 0xff;
                    sensoroBLEData.sensoroE781 = new byte[length + 1];
                    System.arraycopy(data, 0, sensoroBLEData.sensoroE781, 0, length + 1);
                }
            }
        }
        return sensoroBLEData;
    }

    /**
     * 将扫描到的数据解析为byte[] 列表
     *
     * @param scanData
     * @return
     */
    private ArrayList<byte[]> parseScanData2BLEList(byte[] scanData) {
        ArrayList<byte[]> bleArrayList = null;
        if (scanData != null) {
            bleArrayList = new ArrayList<byte[]>();
            for (int i = 0; i < scanData.length; i++) {
                int length = (int) scanData[i] & 0xff;
                if (length == 0) {
                    return bleArrayList;
                } else {
                    byte[] newSensoroBLEData = new byte[length + 1];
                    System.arraycopy(scanData, i, newSensoroBLEData, 0, length + 1);
                    bleArrayList.add(newSensoroBLEData);
                    i = i + length;
                }
            }
        }
        return bleArrayList;
    }

    private byte[] parseBroadcastKey(byte[] keyIdBytes, HashMap<String, byte[]> broadcastKeyMap) {
        byte[] keyBytes = null;
        if (keyIdBytes.length != KEY_ID_LENGTH) {
            return null;
        }
        String keyId = SensoroUtils.bytesToHex(keyIdBytes);
        if (keyId != null && broadcastKeyMap != null) {
            keyBytes = broadcastKeyMap.get(keyId);
        }
        return keyBytes;
    }

    class SensoroBLEData {
        byte[] iBeacon;
        byte[] sensoroE780;
        byte[] sensoroE781;
    }

    class BitFields {
        int transmitPower;
        int advertisingInterval;
        boolean isPasswordEnabled;
        boolean isSecretEnabled;
        boolean isEnergySavingEnabled;
    }
}
